/*
 * $Id$
 */
package org.crazydays.text;


import java.util.List;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.text.ParseException;

/**
 * Arguments
 * <p>
 * Class to process comment line arguments.
 * </p>
 */
public class Arguments
{
	/** dash dash pattern */
	protected static Pattern dashDashPattern = Pattern
			.compile("\\-\\-(\\w+?)(?:\\=(.*?))?\\z");

	/** dash pattern */
	protected static Pattern dashPattern = Pattern
			.compile("\\-(\\w+?)(?:\\=(.*?))?\\z");

	/** arguments */
	protected List<Argument> arguments = new ArrayList<Argument>();

	/**
	 * Arguments constructor.
	 */
	public Arguments()
	{
	}

	/**
	 * Set arguments.
	 * 
	 * @param arguments Arguments
	 */
	public void setArguments(List<Argument> arguments)
	{
		if (arguments == null) {
			throw new IllegalArgumentException("arguments == null");
		}

		this.arguments = arguments;
	}

	/**
	 * Get arguments.
	 * 
	 * @return Arguments
	 */
	public List<Argument> getArguments()
	{
		return arguments;
	}

	/**
	 * Add argument.
	 * 
	 * @param argument Argument
	 * @return Arguments
	 * @throws ArgumentException
	 */
	public Arguments addArgument(Argument argument)
		throws ArgumentException
	{
		if (argument == null) {
			throw new IllegalArgumentException("argument == null");
		}

		boolean stringFound = false;
		boolean characterFound = false;

		// check for duplicates
		for (Argument argumentX : getArguments()) {
			String string = argument.getString();
			if (string != null && argumentX.equals(string)) {
				stringFound = true;
			}

			Character character = argument.getCharacter();
			if (character != null && argumentX.equals(character)) {
				characterFound = true;
			}

			if (stringFound == true || characterFound == true) {
				throw new ArgumentException("Argument ("
						+ argumentX
						+ ") already exists")
						.setArgument(argument);
			}
		}

		// add argument
		getArguments().add(argument);

		return this;
	}

	/**
	 * Get argument.
	 * 
	 * @param string String
	 * @throws ArgumentException
	 */
	public Argument getArgument(String string)
		throws ArgumentException
	{
		for (Argument argument : getArguments()) {
			if (argument.equals(string)) {
				return argument;
			}
		}

		throw new ArgumentException("Undefined argument string: "
				+ string);
	}

	/**
	 * Get argument.
	 * 
	 * @param character Character
	 * @throws ArgumentException
	 */
	public Argument getArgument(Character character)
		throws ArgumentException
	{
		if (character == null) {
			throw new IllegalArgumentException("character == null");
		}

		for (Argument argument : getArguments()) {
			if (argument.equals(character)) {
				return argument;
			}
		}

		throw new ArgumentException("Undefined argument character: "
				+ character);
	}

	/**
	 * Parse arguments.
	 * 
	 * @param arguments Arguments
	 * @throws ParseException
	 * @throws ArgumentException
	 */
	public void parse(String[] arguments)
		throws ParseException, ArgumentException
	{
		if (arguments == null) {
			throw new IllegalArgumentException("arguments == null");
		}


		for (int i = 0; i < arguments.length; i++) {

			if (arguments[i] == null) {
				throw new ParseException("arguments[" + i
						+ "] == null", i);
			} else if (arguments[i].startsWith("--")) {
				i = parseDashDashArgument(arguments, i);
			} else if (arguments[i].startsWith("-")) {
				i = parseDashArgument(arguments, i);
			} else {
				throw new ParseException(
						"Unexpected argument: "
								+ arguments[i],
						i);
			}
		}
	}

	/**
	 * Parse dash dash argument.
	 * 
	 * @param arguments Arguments
	 * @param i Offset
	 * @return Offset
	 * @throws ParseException
	 * @throws ArgumentException
	 */
	protected int parseDashDashArgument(String[] arguments, int i)
		throws ParseException, ArgumentException
	{
		if (arguments == null) {
			throw new IllegalArgumentException("arguments == null");
		}
		if (i < 0 || i >= arguments.length) {
			throw new ArrayIndexOutOfBoundsException("arguments["
					+ i + "]");
		}

		Matcher matcher = dashDashPattern.matcher(arguments[i]);
		if (matcher.matches() == false) {
			throw new ParseException("Unable to match -- string", 0);
		}

		String string = matcher.group(1);
		String value = matcher.group(2);

		if (value == null) {
			if ((i + 1) < arguments.length) {
				if (!arguments[i + 1].startsWith("-")) {
					i++;
					value = arguments[i];
				}
			}

			if (value == null) {
				value = "";
			}
		}

		// set argument value
		Argument argument = getArgument(string);
		argument.setValue(value);

		return i;
	}

	/**
	 * Parse dash argument.
	 * 
	 * @param arguments Arguments
	 * @param i Offset
	 * @return Offset
	 * @throws ParseException
	 * @throws ArgumentException
	 */
	protected int parseDashArgument(String[] arguments, int i)
		throws ParseException, ArgumentException
	{
		if (arguments == null) {
			throw new IllegalArgumentException("arguments == null");
		}
		if (i < 0 || i >= arguments.length) {
			throw new ArrayIndexOutOfBoundsException("arguments["
					+ i + "]");
		}

		Matcher matcher = dashPattern.matcher(arguments[i]);
		if (matcher.matches() == false) {
			throw new ParseException("Unable to match - string", 0);
		}

		Character character = null;
		String characters = matcher.group(1);
		String value = matcher.group(2);

		if (characters.length() == 1) {
			// -a or -a=xyz
			character = characters.charAt(0);

			if (value == null) {
				if ((i + 1) < arguments.length) {
					if (!arguments[i + 1].startsWith("-")) {
						i++;
						value = arguments[i];
					}
				}
			}

			if (value == null) {
				value = "";
			}
		} else {
			if (value != null) {
				throw new ParseException("Unexpected characters after: " +
					characters, i);
			}

			value = "";
		}

		// set argument value
		if (character == null) {
			for (int j = 0; j < characters.length(); j++) {
				Argument argument = getArgument(characters
						.charAt(j));
				argument.setValue(value);
			}
		} else {
			Argument argument = getArgument(character);
			argument.setValue(value);
		}

		return i;
	}

	/**
	 * Validate arguments.
	 * 
	 * @throws ArgumentException
	 * @see Argument#validate()
	 */
	public void validate()
		throws ArgumentException
	{
		for (Argument argument : getArguments()) {
			argument.validate();
		}
	}

	/**
	 * Clear values.
	 *
	 * @see Argument#clearValue()
	 */
	public void clearValues()
	{
		for (Argument argument : getArguments()) {
			argument.clearValue();
		}
	}

	/**
	 * To string.
	 *
	 * @return String
	 * @see Object#toString()
	 */
	public String toString()
	{
		StringBuffer buffer = new StringBuffer();

		for (Argument argument : getArguments()) {
			if (argument.getRequired()) {
				buffer.append(" <--");
				buffer.append(argument.getString());
				buffer.append("|-");
				buffer.append(argument.getCharacter());
				buffer.append(" <=value| value>>");
			} else {
				buffer.append(" [--");
				buffer.append(argument.getString());
				buffer.append("|-");
				buffer.append(argument.getCharacter());
				buffer.append("[=value| value]]");
			}
		}

		return buffer.toString();
	}
}
